package mirrormonkey.util.annotations.parsing.classlevel;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import junit.framework.Assert;
import mirrormonkey.util.annotations.control.AnnotationOverride;
import mirrormonkey.util.annotations.control.IRClass;
import mirrormonkey.util.annotations.parsing.AnnotationParser;
import mirrormonkey.util.annotations.parsing.ClassIR;

import org.junit.Test;

/**
 * Contains test cases concerning the collection of annotations on classes that
 * define neither a hierarchy nor membersById.
 * 
 * @author Philipp Christian Loewner
 * 
 */
public class FlatMemberlessClassIRCollectionTest {

	/**
	 * Keep an instance of <tt>AnnotationParser</tt> around
	 */
	public static AnnotationParser parser = new AnnotationParser();

	/**
	 * Some empty annotation to use for testing
	 */
	@Retention(RetentionPolicy.RUNTIME)
	public static @interface CustomAnnotation {
	}

	/**
	 * Some other empty annotation to use for testing
	 */
	@Retention(RetentionPolicy.RUNTIME)
	public static @interface AnotherCustomAnnotation {
	}

	/**
	 * Tests if it is possible to collect a custom annotation by declaring it in
	 * <tt>IRClass</tt>.
	 */
	@Test
	public void testCollectAnnotationDeclaratively() {
		@CustomAnnotation
		@IRClass(value = ClassIR.class, startCollect = CustomAnnotation.class)
		class DeclareCustomAnnotation {
		}
		ClassIR ir = parser.parseClass(DeclareCustomAnnotation.class, null);
		Assert.assertNotNull(ir.getCollectedAnnotation(CustomAnnotation.class));
	}

	/**
	 * Tests if an annotation that was not declared in <tt>IRClass</tt> will not
	 * be collected even if there is another class that will be collected.
	 */
	@Test
	public void testRejectAnnotationDeclaratively() {
		@CustomAnnotation
		@IRClass(value = ClassIR.class, startCollect = AnotherCustomAnnotation.class)
		class Declare {
		}
		ClassIR ir = parser.parseClass(Declare.class, null);
		Assert.assertNull(ir.getCollectedAnnotation(CustomAnnotation.class));
	}

	/**
	 * This IR class will automatically collect annotations of type
	 * <tt>CustomAnnotation</tt>.
	 */
	public static class DeclareCollectionIR extends ClassIR {
		public DeclareCollectionIR(ClassIR previous, Class<?> forClass) {
			super(previous, forClass);
			addCollectType(CustomAnnotation.class);
		}
	}

	/**
	 * Tests if it is possible to collect a custom annotation by declaring it in
	 * the <tt>ClassIR</tt> that will be used to collect it.
	 */
	@Test
	public void testCollectAnnotationProgramatically() {
		@CustomAnnotation
		@IRClass(value = DeclareCollectionIR.class)
		class Declare {
		}
		ClassIR ir = parser.parseClass(Declare.class, null);
		Assert.assertNotNull(ir.getCollectedAnnotation(CustomAnnotation.class));
	}

	/**
	 * Tests if an annotation that was not declared as collected in the
	 * <tt>ClassIR</tt> will not be collected even if the <tt>ClassIR</tt>
	 * defines other annotations that will be.
	 */
	@Test
	public void testRejectAnnotationProgrammatically() {
		@AnotherCustomAnnotation
		@IRClass(value = DeclareCollectionIR.class)
		class Declare {
		}
		ClassIR ir = parser.parseClass(Declare.class, null);
		Assert.assertNull(ir
				.getCollectedAnnotation(AnotherCustomAnnotation.class));
	}

	/**
	 * Tests if it is possible to combine defining the collection of annotations
	 * declaratively and programmatically.
	 */
	@Test
	public void testCollectAnnotationCombineDeclarations() {
		@CustomAnnotation
		@AnotherCustomAnnotation
		@IRClass(value = DeclareCollectionIR.class, startCollect = AnotherCustomAnnotation.class)
		class Declare {
		}
		ClassIR ir = parser.parseClass(Declare.class, null);
		Assert.assertNotNull(ir.getCollectedAnnotation(CustomAnnotation.class));
		Assert.assertNotNull(ir
				.getCollectedAnnotation(AnotherCustomAnnotation.class));
	}

	/**
	 * Tests if an annotation is still collected despite an override annotation.
	 */
	@Test
	public void testCollectDespiteOverride() {
		@AnnotationOverride
		@CustomAnnotation
		@IRClass(value = ClassIR.class, startCollect = CustomAnnotation.class)
		class DeclareCustomAnnotation {
		}
		ClassIR ir = parser.parseClass(DeclareCustomAnnotation.class, null);
		Assert.assertNotNull(ir.getCollectedAnnotation(CustomAnnotation.class));
	}

	/**
	 * Tests if it is possible to collect a control annotation by declaring it
	 * in the <tt>IRClass</tt> annotation.
	 */
	@Test
	public void testCollectControlAnnotationDeclaratively() {
		@IRClass(value = ClassIR.class, startCollect = IRClass.class)
		class DeclareSelfCollectingAnnotation {
		}
		ClassIR ir = parser.parseClass(DeclareSelfCollectingAnnotation.class,
				null);
		Assert.assertNotNull(ir.getCollectedAnnotation(IRClass.class));
	}

	/**
	 * This IR class will automatically collect annotations of type
	 * <tt>IRClass</tt>.
	 */
	public static class DeclareSelfCollectingIR extends ClassIR {
		public DeclareSelfCollectingIR(ClassIR previous, Class<?> forClass) {
			super(previous, forClass);
			addCollectType(IRClass.class);
		}
	}

	/**
	 * Tests if it is possible to collect a control annotation by declaring it
	 * in the <tt>ClassIR</tt> that will be used to collect it.
	 */
	@Test
	public void testCollectControlAnnotationProgramatically() {
		@IRClass(DeclareSelfCollectingIR.class)
		class Declared {
		}
		ClassIR ir = parser.parseClass(Declared.class, null);
		Assert.assertNotNull(ir.getCollectedAnnotation(IRClass.class));
	}

	/**
	 * Tests if it is possible to collect two custom annotations by declaring it
	 * in the <tt>IRClass</tt> annotation
	 */
	@Test
	public void testCollectMultiple() {
		@CustomAnnotation
		@AnotherCustomAnnotation
		@IRClass(value = ClassIR.class, startCollect = {
				CustomAnnotation.class, AnotherCustomAnnotation.class })
		class Declared {
		}
		ClassIR ir = parser.parseClass(Declared.class, null);
		Assert.assertNotNull(ir.getCollectedAnnotation(CustomAnnotation.class));
		Assert.assertNotNull(ir
				.getCollectedAnnotation(AnotherCustomAnnotation.class));
	}

	/**
	 * Tests if exactly the annotation that should be collected is collected if
	 * multiple are present
	 */
	@Test
	public void testCollectSomeRejectSome() {
		@CustomAnnotation
		@AnotherCustomAnnotation
		@IRClass(value = ClassIR.class, startCollect = CustomAnnotation.class)
		class Declared {
		}
		ClassIR ir = parser.parseClass(Declared.class, null);
		Assert.assertNotNull(ir.getCollectedAnnotation(CustomAnnotation.class));
		Assert.assertNull(ir
				.getCollectedAnnotation(AnotherCustomAnnotation.class));
	}

}
